//
//  ZBBaseRequest.m
//  ZBDay
//
//  Created by xzb on 2018/2/7.
//  Copyright © 2018年 xzb. All rights reserved.
//

#import "ZBBaseRequest.h"
#import "ZBNetworkCache.h"
#import "MJExtension/MJExtension.h"
#import "ZBDataManager.h"
#import <YYCategories/NSString+YYAdd.h>

@interface ZBBaseRequest ()

@property (nonatomic, strong) NSURLSessionTask *task;

@property (nonatomic, strong) dispatch_source_t watchdogTimer;

@property (nonatomic, strong) ZBNetworkCache *networkCache;

@end

@implementation ZBBaseRequest

+ (instancetype)request
{
    ZBBaseRequest *request = [[[self class] alloc] init];
    return request;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.priority = ZB_REQ_PRIORITY_NORMAL;
        self.fields = [NSMutableDictionary dictionary];
        self.networkCache = [ZBNetworkCache instance];
    }
    return self;
}
- (void)sendQuery
{
    @weakify(self);
    if (self.priority == ZB_REQ_PRIORITY_NORMAL) {
        [self showHUD:YES];
    }
    
    if (self.shouldCache) {
        // 如果使用缓存且命中，先回调一次
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSDictionary *cachedDictionary = [self cachedResponseDictionary];
            id dataModel = [self resultWithResponse:cachedDictionary];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (dataModel) {
                    [self handleCacheResult:dataModel];
                }
            });
        });
    }
    
    self.task = [ZBNetworkManager sendRequestType:self.httpType urlStr:self.apiUrl params:self.fields  headers:self.headers success:^(ZBResponseModel *result) {
        
        @strongify(self);
        [self stopWatchDog];
        [self requestCompletedWithSuccess:result];
        [self saveNetworkLogWithModel:result];
    } failure:^(id error) {
        @strongify(self);
        [self stopWatchDog];
        [self requestCompletedWithError:error];
    }];
}
- (void)cancelRequest
{
    [self stopWatchDog];
    [self.task cancel];
    self.task = nil;
}

- (BOOL)isExecuting
{
    return self.task && self.task.state == NSURLSessionTaskStateRunning;
}

#pragma mark - completion callbacks

- (void)requestCompletedWithSuccess:(ZBResponseModel *)model
{
    @onExit
    {
        self.task = nil;
    };
   
    NSDictionary *responseDictionary = model.responseJson;
    NSError *error;
    BOOL stop = NO;
    if (!model || !model.responseJson || ![responseDictionary isKindOfClass:[NSDictionary class]]) {
        error = [NSError ZB_errorWithCode:1 errorMessage:@"服务器不乖了，等会再试试"];
        stop = YES;
    }
    if (stop) {
        if (self.onError && error != nil) {
            self.onError(error);
        } else {
            if (error) {
                [ZBHUD showError:error.userInfo[@"error_msg"]];
            } else {
                [ZBHUD showError:@"服务器不乖了，等会再试试"];
            }
        }
        return;
    }
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (self.shouldCache) {
            [self saveToCacheWithResponseDictionary:responseDictionary];
        }
        
        id result = [self resultWithResponse:responseDictionary];
        if (!result) {
            result = responseDictionary;
            //            result = [NSError ZB_errorWithCode:1 errorMessage:[NSString stringWithFormat:@"modelClass :%@ 转换有误", self.modelName]];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [self handleSuccessResult:result];
        });
    });
}

- (void)handleCacheResult:(id)result
{
    if (self.onLoadFromCache) {
        self.onLoadFromCache(result);
    } else if (self.onCompletion) {
        self.onCompletion(result);
    }
}
- (void)handleSuccessResult:(id)result
{
    if (self.onCompletion) {
        self.onCompletion(result);
    }
}

- (id)resultWithResponse:(NSDictionary *)responseDictionary
{
    Class modelClass = NSClassFromString(self.modelName);
    return  [modelClass mj_objectWithKeyValues:responseDictionary];
}

- (void)requestCompletedWithError:(NSError *)error
{
    @onExit
    {
        self.task = nil;
        [self showHUD:NO];
    };
    
    if (self.onError) {
        self.onError(error);
    }
}

#pragma mark - 监视超时

- (void)startWatchDogWithType:(NSString *)verb
                          url:(NSString *)urlString
                       params:(NSDictionary *)params
{
    [self stopWatchDog];
    static uint64_t timeout = 30; // 秒
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
    dispatch_source_set_event_handler(timer, ^{
        [self stopWatchDog];
    });
    self.watchdogTimer = timer;
    dispatch_resume(timer);
}

- (void)stopWatchDog
{
    __strong dispatch_source_t timer = self.watchdogTimer;
    if (timer != NULL) {
        dispatch_source_cancel(timer);
        self.watchdogTimer = nil;
    }
    [self showHUD:NO];
}
#pragma mark - local cache

- (NSString *)uniqueIdentifier
{
    NSMutableString *filename = [[NSMutableString alloc] init];
    [filename appendString:self.method];
    [self.fields enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
        [filename appendFormat:@"%@%@", key, obj];
    }];
    return [filename md5String];
}

- (void)saveToCacheWithResponseDictionary:(NSDictionary *)responseDictionary
{
    NSData *data = [NSJSONSerialization dataWithJSONObject:responseDictionary options:0 error:nil];
    [self.networkCache setData:data forKey:self.uniqueIdentifier];
}

- (NSDictionary *)cachedResponseDictionary
{
    NSData *cacheData = [[self.networkCache dataForKey:self.uniqueIdentifier] copy];
    if (!cacheData) {
        return nil;
    }
    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:cacheData options:0 error:nil];
    return result;
}

#pragma mark - toast
- (void)showHUD:(BOOL)show
{
    if (!self.showHUD) {
        return;
    }
    if (self.priority == ZB_REQ_PRIORITY_LOW ){
        return;
    }
    show ? [ZBHUD show] : [ZBHUD hidden];
}

#pragma mark - log
- (void)saveNetworkLogWithModel:(ZBResponseModel *)model
{
    NSString *key = model.response.URL.absoluteString;
    if (!model.responseJson) {
        if (!model.responseData) {
            return;
        }
        NSString *errorStr = [[NSString alloc] initWithData:model.responseData encoding:NSUTF8StringEncoding];
        if (errorStr.length > 0) {
            [[ZBDataManager instance] setData:errorStr forKey:key];
        }
        return;
    }
    [[ZBDataManager instance] setData:model.responseJson forKey:key];
}
@end
@implementation ZBBaseRequest (Subscript)

- (id)objectForKeyedSubscript:(id)key; //getter
{
    return self.fields[[key lowercaseString]];
}

- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key; //setter
{
    NSString *keyStr = (NSString *)key;
    self.fields[[keyStr lowercaseString]] = obj;
}

@end
